---
description: >
  Basic security for HTML and JSON response bodies.
title: Secure Response Body
---

_Check out [secure web development](/cheat-sheets/secure-web-development){:rel="noopener"} and
[secure response headers](/cheat-sheets/secure-response-headers){:rel="noopener"} for more notes._

This cheat sheet only covers HTML and JSON response bodies. Sample code uses
a combination of slim, and pseudo code. Single letter methods `u`, `j` escape
their argument's contents.

<details markdown="1" id="table-of-contents">
<summary>
Table of Contents
</summary>

* TOC
{:toc}
</details>

## Handling input

Display input, and user-controlled data securely:

- Only inject data in whitelisted locations.
- Ensure the `Content-Type` header matches the body.
- Interpolate sanitized data when building up output.
- Escape user-controlled, and input-based data according to its output type.
- Only escape data in an output context for data integrity.

```slim
src="https://#{ u(uri.host + uri.path) }"
```

## JSON response

- Avoid embedding scripts.
- Escape all values:
  + Input.
  + User-controlled.

```slim
= j hsh.to_json
```

## HTML Response

### Head
- Set more granular robot rules.

```slim
meta name="robots" content="noindex, follow"
```

- Set a more detailed referrer policy.

```slim
meta name="referrer" content="origin"
```

- Expose CSRF tokens to use in secure Ajax calls.

```slim
= csrf_metatag

/ short for:

meta name="_csrf" content="#{ csrf_token }"
```

Avoid caching pages with `csrf-token` named `meta` tags. Instead, retrieve
the token when users perform the first non `GET` request as a pre-flight
`GET` request to `session/csrf`.


#### Subresource Integrity

Secure CDN loaded assets by adding the `integrity` attribute to `script`s and `link`s.
To prevent attackers from reading data cross-origin we must add the `crossorigin`
attribute with either `anonymous` or `use-credentials` value.

```slim
script[
    src="https://cdn.example.com/in-vogue.js"
    integrity="sha256-oqV...= sha512-zM0...=="
    crossorigin="anonymous"
  ]
```

If our CDN provider doesn't provide integrity hashes we can generate them using
[httpie](https://github.com/jakubroztocil/httpie){:rel="nofollow noreferrer noopener"}
and openssl:

```terminal
$ http https://trusted.cdn.com/path/to/asset.css | openssl dgst -sha512 -binary | openssl enc -base64 -A
```

In the first example we included both the sha512, and the less secured sha256 for
compatibility with older browsers. Modern browsers will check against the most
secured version by default.


### Body

#### Links
- Add privacy settings on all links (`a` elements) pointing to external services.

```slim
a href="https://example.com" rel="nofollow noreferrer noopener"
```

If for SEO the site requires links (`a`) to be followed merely remove `nofollow`.
Keeping `noopener` to help prevent tabnabbing attacks, [won't affect SEO](https://blog.templatetoaster.com/rel-noreferrer-noopener-seo/){:rel="nofollow noreferrer noopener"}.
`noreferrer` is a fallback for [browsers](https://caniuse.com/#search=noopener){:rel="nofollow noreferrer noopener"}
that still don't support `noopener`.

- Use even more detailed referrer policies.

```slim
a href="http://example.com/" referrerPolicy="origin"
  | click me
```

#### Form AutoComplete
- Turn autocomplete off for sensitive input.

```slim
input type="text" name="passport-number" autocomplete="off"
```

[Modern browsers](https://caniuse.com/#search=autocomplete){:rel="nofollow noreferrer noopener"}
ignore the `off` option
for inputs type `username`, `password`, `current-password` so credential managers
can auto fill them.

#### Cross Site Request Forgery

- Add security tokens in forms and links that trigger changes to prevent CSRF.

```slim
form action="/replace" id="replace" method="post"
  input name="_method" type="hidden" value="put"
  = csrf_tag
```

- Test unique identifiers are appended to:
  + non-idempotent requests.
  + links.
  + forms.

#### Cross-Site Scripting (XSS)

When for legitimate reasons we allow input and/or end-users
to manipulate data into structured, and styled content:

- Escape both according to their output type.

##### HTML

- Support input in other markup languages. eg. markdown, liquid.
- Escape these before injecting them into HTML elements:
  + User-controlled content.
  + Input-generated content.

```slim
h1
  = user_controlled.content
div
  = input_generated.content
```

- Escape input, and user-manipulated content before using it as an attribute's value.

```slim
p attr="#{ attribute.value }"
  | content
```

Some elements can NEVER be consider safe enough to inject input and/or
user-manipulated content to.

```slim
script
  = inline_scripts
/!
  = inline_comments
div "#{ as_attribute_name }"=test
div
  "#{ as_tag_name }".some-class
style
  = inline_styling
```

##### CSS

- Use security-focused gems to escape CSS before injecting it.

Some CSS contexts can NEVER be consider to save to use with input even if
properly escaped. (Pseudo code)

```css
{ background-url: "URL_TO_MALICIOUS_JS"; } // same for other attributes that can take URLs
{ text-size: "expression(MALICIOUS_JS)"; } // only in IE
```

##### ECMAScript (JS)

- Avoid embedding input-based ECMAScript (JS) to prevent malicious scripts.
- Escape input based, and user-controlled scripts with help of specialized libraries
  when it can't be avoid.

Some expressions that will NEVER handle input safely:

```js
window.setInterval('HERE_WE_ALWAYS_GET_XSSED');
```

#### Special Considerations

##### BREACH

This category of attacks doesn't affect a specific piece of software. Web apps
compressing HTTP body responses (eg. gzip, brotli), whilst reflecting user provided
content, and secrets (eg. CSRF tokens, sensitive data) are likely to be vulnerable.

The recommended mitigation practices, in order of effectiveness, are:

- Disable HTTP compression.
- Separate secrets from user input.
- Randomize secrets per request.
- Mask secrets.
  + XORG w/random request-secret.
- Use CSRF tokens.
- Randomly byte-pad responses to hide their length.
- Rate-limit requests.

Keep in mind that the practicality of each mitigation varies from one app to another.

## Resources

_Check out [secure web development](/cheat-sheets/secure-web-development){:rel="noopener"} and
[secure response headers](/cheat-sheets/secure-response-headers){:rel="noopener"} for more resources._

- [HTML Escaping](https://wonko.com/post/html-escaping){:rel="nofollow noreferrer noopener"}.
- [Cross-Site Request Forgery](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)){:rel="nofollow noreferrer noopener"}.
- [Subresource integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity){:rel="nofollow noreferrer noopener"}.
- [SRI and CORS](https://w3c.github.io/webappsec-subresource-integrity/#is-response-eligible){:rel="nofollow noreferrer noopener"}
- [An Introduction to ERB Templating](https://www.stuartellis.name/articles/erb/){:rel="nofollow noreferrer noopener"}.
- [Testing for CSRF](https://www.owasp.org/index.php/Testing_for_CSRF_(OTG-SESS-005)){:rel="nofollow noreferrer noopener"}.
- [Input Autocomple Attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-autocomplete){:rel="nofollow noreferrer noopener"}.
- [rel="noreferrer noopener" SEO Issues?](https://blog.templatetoaster.com/rel-noreferrer-noopener-seo/){:rel="nofollow noreferrer noopener"}
- [target="_blank" - The Most Underestimated Vulnerability Ever](https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/){:rel="nofollow noreferrer noopener"}
- [Referrer Policies](https://www.w3.org/TR/referrer-policy/#referrer-policies){:rel="nofollow noreferrer noopener"}
- [About rel=noopener](https://mathiasbynens.github.io/rel-noopener/){:rel="nofollow noreferrer noopener"}
- [The Performance Benefits of rel=noopener](https://jakearchibald.com/2016/performance-benefits-of-rel-noopener/){:rel="nofollow noreferrer noopener"}
- [BREACH Attack Mitigations](https://web.archive.org/web/20180401152815/http://breachattack.com/#mitigations){:rel="nofollow noreferrer noopener"}
